/*
 * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang;

import java.io.PrintStream;
import java.util.Arrays;

import sun.misc.VM;

/**
 * 线程组,就是由线程组成的管理线程的类
 * <p> 1、在Java中每一个线程都归属于某个线程组管理的一员，例如在主函数main()主工作流程中产生一个线程，则产生的线程属于main这个线程组管理的一员。
 * <p> 2、线程组表示一组线程。此外，线程组还可以包括其他线程组。线程组形成一个树，其中除了初始线程组之外的每个线程组都有一个父线程。
 * <p> 3、允许线程访问有关其自身线程组的信息，但不能访问有关其线程组的父线程组或任何其他线程组的相关信息。
 */
public
class ThreadGroup implements Thread.UncaughtExceptionHandler {

    /*
     * (1)线程组也可以包含其他线程组。如上面的groups[].
     * (2)线程组构成一棵树，在树中，除了初始线程组外，每个线程组都有一个父线程组
     */
    private final ThreadGroup parent;         // 父线程组
    String name;                              // 线程组名称
    int maxPriority;                          // 线程组最大优先级
    boolean destroyed;                        // 销毁状态
    boolean daemon;                           // 守护线程组表示: true-守护线程组;false-普通线程组
    boolean vmAllowSuspension;                // 虚拟机自动挂起【属性是一个标识符，用于表示当前线程组中的所有线程是否可以被挂起。
    int nUnstartedThreads = 0;                // 未启动线程数(EW状态的线程数)
    int nthreads;                             // 线程总数【仅统计当前线程组中的活动线程数量，不包括子线程组中的线程数量】
    Thread threads[];                         // 线程数组【表示当前线程组中的所有线程，它是一个 Thread 类型的数组，包含了当前线程组中正在运行或等待运行的所有线程】
    int ngroups;                              // 线程组数量,【仅统计当前线程组的子线程组数量，不包括子线程组的子线程组数量】
    ThreadGroup groups[];                     // 线程组数组


    private ThreadGroup() {
        this.name = "system";
        this.maxPriority = Thread.MAX_PRIORITY;
        this.parent = null;
    }

    public ThreadGroup(String name) {
        this(Thread.currentThread().getThreadGroup(), name);
    }

    public ThreadGroup(ThreadGroup parent, String name) {
        this(checkParentAccess(parent), parent, name);
    }

    private ThreadGroup(Void unused, ThreadGroup parent, String name) {
        this.name = name;
        this.maxPriority = parent.maxPriority;
        this.daemon = parent.daemon;
        this.vmAllowSuspension = parent.vmAllowSuspension;
        this.parent = parent;
        parent.add(this);
    }


    /**
     * 检查调用线程组的访问权限。
     * 该方法用于确保当前线程有权限访问其父线程组。如果没有适当的权限，
     * 调用线程可能会被安全地中断或限制。
     * @param parent 要检查访问权限的父线程组。
     * @return 由于方法逻辑上总是返回null，因此此返回值仅用于方法签名，实际开发中不建议这样设计。
     */
    private static Void checkParentAccess(ThreadGroup parent) {
        // 检查父线程组的访问权限。
        parent.checkAccess();
        return null;
    }

    /**
     * 返回此线程组的名称
     */
    public final String getName() {
        return name;
    }

    /**
     * 返回此线程组的父级
     */
    public final ThreadGroup getParent() {
        if (parent != null)
            parent.checkAccess();
        return parent;
    }

    /**
     * 返回此线程组的最大优先级
     */
    public final int getMaxPriority() {
        return maxPriority;
    }


    public final boolean isDaemon() {
        return daemon;
    }

    /**
     * 测试此线程组是否已被破坏。
     */
    public synchronized boolean isDestroyed() {
        return destroyed;
    }

    /**
     * 更改此线程组的守护程序状态。
     */
    public final void setDaemon(boolean daemon) {
        checkAccess();
        this.daemon = daemon;
    }


    /**
     * 设置线程组的最大优先级
     */
    public final void setMaxPriority(int pri) {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;

        synchronized (this) {
            checkAccess();
            if (pri < Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY) {
                return;
            }
            maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri;
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }

        for (int i = 0; i < ngroupsSnapshot; i++) {
            groupsSnapshot[i].setMaxPriority(pri);
        }
    }

    /**
     * 测试此线程组是线程组参数还是其祖先线程组之一
     */
    public final boolean parentOf(ThreadGroup g) {
        for (; g != null; g = g.parent) {
            if (g == this) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查当前线程是否可以修改当前线程组及其子线程组的信息
     */
    public final void checkAccess() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkAccess(this);
        }
    }

    /**
     * 返回此线程组及其子组中活动线程数得估计
     */
    public int activeCount() {
        int result;
        // Snapshot sub-group data so we don't hold this lock
        // while our children are computing.
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            result = nthreads;
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0; i < ngroupsSnapshot; i++) {
            result += groupsSnapshot[i].activeCount();
        }
        return result;
    }


    /**
     * 返回此线程组及其子组中活动组数的估计
     */
    public int activeGroupCount() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        int n = ngroupsSnapshot;
        for (int i = 0; i < ngroupsSnapshot; i++) {
            n += groupsSnapshot[i].activeGroupCount();
        }
        return n;
    }


    /**
     * 销毁当前线程组及所有子线程组和线程
     * <p> 1、首先获取当前线程组的子线程组和子线程的快照，以便在销毁子线程组时使用。
     * <p> 2、检查当前线程组是否已经被销毁或者线程组中还有正在运行的线程，如果是则抛出 IllegalThreadStateException 异常
     * <p> 3、如果当前线程组有子线程组，则递归调用子线程组的 destroy() 方法进行销毁
     * <p> 4、如果当前线程组有父线程组，则从父线程组中移除当前线程组。
     * <p> 5、销毁当前线程组，将 destroyed 属性设置为 true，并将线程组中的线程数和子线程组数都设置为 0，将线程组和线程数组都设置为 null。
     */
    public final void destroy() {

        // 当前线程组下的子线程组数量的快照
        int ngroupsSnapshot;
        // 当前线程组下的子线程组的快照
        ThreadGroup[] groupsSnapshot;


        synchronized (this) {
            // 检查当前线程是否可以修改当前线程组及其子线程组的信息
            checkAccess();
            if (destroyed || (nthreads > 0)) {
                throw new IllegalThreadStateException();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
            if (parent != null) {
                destroyed = true;
                ngroups = 0;
                groups = null;
                nthreads = 0;
                threads = null;
            }
        }


        for (int i = 0; i < ngroupsSnapshot; i += 1) {
            groupsSnapshot[i].destroy();
        }
        if (parent != null) {
            parent.remove(this);
        }
    }


    /**
     * 将此线程组中的每个活动线程复制到指定的数组中。
     */
    public int enumerate(Thread list[]) {
        checkAccess();
        return enumerate(list, 0, true);
    }

    /**
     * 将此线程组中的每个活动线程复制到指定的数组中。
     */
    public int enumerate(Thread list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
    }

    private int enumerate(Thread list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int nt = nthreads;
            if (nt > list.length - n) {
                nt = list.length - n;
            }
            for (int i = 0; i < nt; i++) {
                if (threads[i].isAlive()) {
                    list[n++] = threads[i];
                }
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0; i < ngroupsSnapshot; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }


    /**
     * Copies into the specified array references to every active
     * subgroup in this thread group and its subgroups.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #enumerate(ThreadGroup[], boolean) enumerate}{@code (list, true)}
     * </blockquote>
     *
     * @param list an array into which to put the list of thread groups
     * @return the number of thread groups put into the array
     * @throws SecurityException if {@linkplain #checkAccess checkAccess} determines that
     *                           the current thread cannot access this thread group
     * @since JDK1.0
     */
    public int enumerate(ThreadGroup list[]) {
        checkAccess();
        return enumerate(list, 0, true);
    }

    /**
     * Copies into the specified array references to every active
     * subgroup in this thread group. If {@code recurse} is
     * {@code true}, this method recursively enumerates all subgroups of this
     * thread group and references to every active thread group in these
     * subgroups are also included.
     *
     * <p> An application might use the
     * {@linkplain #activeGroupCount activeGroupCount} method to
     * get an estimate of how big the array should be, however <i>if the
     * array is too short to hold all the thread groups, the extra thread
     * groups are silently ignored.</i>  If it is critical to obtain every
     * active subgroup in this thread group, the caller should verify that
     * the returned int value is strictly less than the length of
     * {@code list}.
     *
     * <p> Due to the inherent race condition in this method, it is recommended
     * that the method only be used for debugging and monitoring purposes.
     *
     * @param list    an array into which to put the list of thread groups
     * @param recurse if {@code true}, recursively enumerate all subgroups
     * @return the number of thread groups put into the array
     * @throws SecurityException if {@linkplain #checkAccess checkAccess} determines that
     *                           the current thread cannot access this thread group
     * @since JDK1.0
     */
    public int enumerate(ThreadGroup list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
    }

    private int enumerate(ThreadGroup list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int ng = ngroups;
            if (ng > list.length - n) {
                ng = list.length - n;
            }
            if (ng > 0) {
                System.arraycopy(groups, 0, list, n, ng);
                n += ng;
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0; i < ngroupsSnapshot; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }

    /**
     * Stops all threads in this thread group.
     * <p>
     * First, the <code>checkAccess</code> method of this thread group is
     * called with no arguments; this may result in a security exception.
     * <p>
     * This method then calls the <code>stop</code> method on all the
     * threads in this thread group and in all of its subgroups.
     *
     * @throws SecurityException if the current thread is not allowed
     *                           to access this thread group or any of the threads in
     *                           the thread group.
     * @see java.lang.SecurityException
     * @see java.lang.Thread#stop()
     * @see java.lang.ThreadGroup#checkAccess()
     * @since JDK1.0
     * @deprecated This method is inherently unsafe.  See
     * {@link Thread#stop} for details.
     */
    @Deprecated
    public final void stop() {
        if (stopOrSuspend(false))
            Thread.currentThread().stop();
    }

    /**
     * 中断此线程组中的所有线程
     */
    public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0; i < nthreads; i++) {
                threads[i].interrupt();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0; i < ngroupsSnapshot; i++) {
            groupsSnapshot[i].interrupt();
        }
    }

    /**
     * Suspends all threads in this thread group.
     * <p>
     * First, the <code>checkAccess</code> method of this thread group is
     * called with no arguments; this may result in a security exception.
     * <p>
     * This method then calls the <code>suspend</code> method on all the
     * threads in this thread group and in all of its subgroups.
     *
     * @throws SecurityException if the current thread is not allowed
     *                           to access this thread group or any of the threads in
     *                           the thread group.
     * @see java.lang.Thread#suspend()
     * @see java.lang.SecurityException
     * @see java.lang.ThreadGroup#checkAccess()
     * @since JDK1.0
     * @deprecated This method is inherently deadlock-prone.  See
     * {@link Thread#suspend} for details.
     */
    @Deprecated
    @SuppressWarnings("deprecation")
    public final void suspend() {
        if (stopOrSuspend(true))
            Thread.currentThread().suspend();
    }

    /**
     * Helper method: recursively stops or suspends (as directed by the
     * boolean argument) all of the threads in this thread group and its
     * subgroups, except the current thread.  This method returns true
     * if (and only if) the current thread is found to be in this thread
     * group or one of its subgroups.
     */
    @SuppressWarnings("deprecation")
    private boolean stopOrSuspend(boolean suspend) {
        boolean suicide = false;
        Thread us = Thread.currentThread();
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            checkAccess();
            for (int i = 0; i < nthreads; i++) {
                if (threads[i] == us)
                    suicide = true;
                else if (suspend)
                    threads[i].suspend();
                else
                    threads[i].stop();
            }

            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            }
        }
        for (int i = 0; i < ngroupsSnapshot; i++)
            suicide = groupsSnapshot[i].stopOrSuspend(suspend) || suicide;

        return suicide;
    }

    /**
     * Resumes all threads in this thread group.
     * <p>
     * First, the <code>checkAccess</code> method of this thread group is
     * called with no arguments; this may result in a security exception.
     * <p>
     * This method then calls the <code>resume</code> method on all the
     * threads in this thread group and in all of its sub groups.
     *
     * @throws SecurityException if the current thread is not allowed to
     *                           access this thread group or any of the threads in the
     *                           thread group.
     * @see java.lang.SecurityException
     * @see java.lang.Thread#resume()
     * @see java.lang.ThreadGroup#checkAccess()
     * @since JDK1.0
     * @deprecated This method is used solely in conjunction with
     * <tt>Thread.suspend</tt> and <tt>ThreadGroup.suspend</tt>,
     * both of which have been deprecated, as they are inherently
     * deadlock-prone.  See {@link Thread#suspend} for details.
     */
    @Deprecated
    @SuppressWarnings("deprecation")
    public final void resume() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0; i < nthreads; i++) {
                threads[i].resume();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0; i < ngroupsSnapshot; i++) {
            groupsSnapshot[i].resume();
        }
    }

    /**
     * Adds the specified Thread group to this group.
     *
     * @param g the specified Thread group to be added
     * @throws IllegalThreadStateException If the Thread group has been destroyed.
     */
    private final void add(ThreadGroup g) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (groups == null) {
                groups = new ThreadGroup[4];
            } else if (ngroups == groups.length) {
                groups = Arrays.copyOf(groups, ngroups * 2);
            }
            groups[ngroups] = g;

            // This is done last so it doesn't matter in case the
            // thread is killed
            ngroups++;
        }
    }

    /**
     * Removes the specified Thread group from this group.
     *
     * @param g the Thread group to be removed
     * @return if this Thread has already been destroyed.
     */
    private void remove(ThreadGroup g) {
        synchronized (this) {
            if (destroyed) {
                return;
            }
            for (int i = 0; i < ngroups; i++) {
                if (groups[i] == g) {
                    ngroups -= 1;
                    System.arraycopy(groups, i + 1, groups, i, ngroups - i);
                    // Zap dangling reference to the dead group so that
                    // the garbage collector will collect it.
                    groups[ngroups] = null;
                    break;
                }
            }
            if (nthreads == 0) {
                notifyAll();
            }
            if (daemon && (nthreads == 0) &&
                    (nUnstartedThreads == 0) && (ngroups == 0)) {
                destroy();
            }
        }
    }


    /**
     * Increments the count of unstarted threads in the thread group.
     * Unstarted threads are not added to the thread group so that they
     * can be collected if they are never started, but they must be
     * counted so that daemon thread groups with unstarted threads in
     * them are not destroyed.
     */
    void addUnstarted() {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            nUnstartedThreads++;
        }
    }

    /**
     * Adds the specified thread to this thread group.
     *
     * <p> Note: This method is called from both library code
     * and the Virtual Machine. It is called from VM to add
     * certain system threads to the system thread group.
     *
     * @param t the Thread to be added
     * @throws IllegalThreadStateException if the Thread group has been destroyed
     */
    void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;

            // This is done last so it doesn't matter in case the
            // thread is killed
            nthreads++;

            // The thread is now a fully fledged member of the group, even
            // though it may, or may not, have been started yet. It will prevent
            // the group from being destroyed so the unstarted Threads count is
            // decremented.
            nUnstartedThreads--;
        }
    }

    /**
     * Notifies the group that the thread {@code t} has failed
     * an attempt to start.
     *
     * <p> The state of this thread group is rolled back as if the
     * attempt to start the thread has never occurred. The thread is again
     * considered an unstarted member of the thread group, and a subsequent
     * attempt to start the thread is permitted.
     *
     * @param t the Thread whose start method was invoked
     */
    void threadStartFailed(Thread t) {
        synchronized (this) {
            remove(t);
            nUnstartedThreads++;
        }
    }

    /**
     * Notifies the group that the thread {@code t} has terminated.
     *
     * <p> Destroy the group if all of the following conditions are
     * true: this is a daemon thread group; there are no more alive
     * or unstarted threads in the group; there are no subgroups in
     * this thread group.
     *
     * @param t the Thread that has terminated
     */
    void threadTerminated(Thread t) {
        synchronized (this) {
            remove(t);

            if (nthreads == 0) {
                notifyAll();
            }
            if (daemon && (nthreads == 0) &&
                    (nUnstartedThreads == 0) && (ngroups == 0)) {
                destroy();
            }
        }
    }

    /**
     * Removes the specified Thread from this group. Invoking this method
     * on a thread group that has been destroyed has no effect.
     *
     * @param t the Thread to be removed
     */
    private void remove(Thread t) {
        synchronized (this) {
            if (destroyed) {
                return;
            }
            for (int i = 0; i < nthreads; i++) {
                if (threads[i] == t) {
                    System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
                    // Zap dangling reference to the dead thread so that
                    // the garbage collector will collect it.
                    threads[nthreads] = null;
                    break;
                }
            }
        }
    }


    /**
     * 将有关此线程组的信息打印到标准输出
     */
    public void list() {
        list(System.out, 0);
    }

    void list(PrintStream out, int indent) {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            for (int j = 0; j < indent; j++) {
                out.print(" ");
            }
            out.println(this);
            indent += 4;
            for (int i = 0; i < nthreads; i++) {
                for (int j = 0; j < indent; j++) {
                    out.print(" ");
                }
                out.println(threads[i]);
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0; i < ngroupsSnapshot; i++) {
            groupsSnapshot[i].list(out, indent);
        }
    }

    /**
     * 当此线程组中的线程因为一个未捕获的异常由Java Virtual Machine调用，而线程不具有特定Thread.UncaughtExceptionHandler安装。
     */
    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                    Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                        + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

    /**
     * Used by VM to control lowmem implicit suspension.
     *
     * @param b boolean to allow or disallow suspension
     * @return true on success
     * @since JDK1.1
     * @deprecated The definition of this call depends on {@link #suspend},
     * which is deprecated.  Further, the behavior of this call
     * was never specified.
     */
    @Deprecated
    public boolean allowThreadSuspension(boolean b) {
        this.vmAllowSuspension = b;
        if (!b) {
            VM.unsuspendSomeThreads();
        }
        return true;
    }


    public String toString() {
        return getClass().getName() + "[name=" + getName() + ",maxpri=" + maxPriority + "]";
    }
}
